package com.wordpress.music2dot0.server;

import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Timer;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

@XmlRootElement(name = "data")
public class Playlist {
	private final static String API_URL = "http://api.jamendo.com/get2/";
	private final static String ITEM_UNIT = "id+name+url+stream+duration"
			+ "+album_name+artist_name+tag_idstr/";
	private final static String LOAD_TYPE = "track/";
	private final static String FORMAT_NAME = "xml/";
	private final static String TARGET_NAME = "track_album+album_artist/";

	public static Playlist create(int size) {
		// FIXME: filling the list randomly gives NullPointerExceptions
		try {
			final URL xmlLocation = new URL(API_URL + ITEM_UNIT + LOAD_TYPE
					+ FORMAT_NAME + TARGET_NAME + "?n=" + size
					+ "&streamencoding=mp31");
			return create(xmlLocation);
		} catch (final MalformedURLException e) {
			LoginServer.log.throwing("Playlist", "create", e);
		}
		return null;
	}

	public static Playlist create(int size, String genre) {
		try {
			final URL xmlLocation = new URL(API_URL + ITEM_UNIT + LOAD_TYPE
					+ FORMAT_NAME + TARGET_NAME + "?n=" + size + "&tag_idstr="
					+ genre + "&order=random&streamencoding=mp31");
			return create(xmlLocation);
		} catch (final MalformedURLException e) {
			LoginServer.log.throwing("Playlist", "create", e);
		}
		return null;
	}

	private static Playlist create(URL xmlLocation) {
		Playlist playlist = null;
		try {
			final JAXBContext jaxbContext = JAXBContext
					.newInstance(Playlist.class);
			final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
			playlist = (Playlist) unmarshaller.unmarshal(xmlLocation);
			LoginServer.log.info("New playlist: " + playlist.toString());
		} catch (final JAXBException e) {
			LoginServer.log.throwing("Playlist", "create", e);
		}
		return playlist;
	}

	@XmlElement
	private Song current;

	@XmlElement
	Deque<Song> track = new ArrayDeque<Song>();

	Timer playTimer = new Timer();

	public void addGenre(int size, String genre) {
		this.merge(create(size, genre));
	}

	public Song getCurrent() {
		return this.current;
	}

	public Document marshal() {
		try {
			final JAXBContext jaxbContext = JAXBContext
					.newInstance(Playlist.class);
			final Marshaller marshaller = jaxbContext.createMarshaller();
			final DocumentBuilderFactory dbf = DocumentBuilderFactory
					.newInstance();
			final DocumentBuilder db = dbf.newDocumentBuilder();
			final Document doc = db.newDocument();
			marshaller.marshal(this, doc);
			return doc;
		} catch (final JAXBException e) {
			e.printStackTrace();
		} catch (final ParserConfigurationException e) {
			e.printStackTrace();
		}
		return null;
	}

	public void merge(Playlist p) {
		this.track.addAll(p.track);
	}

	public void playNext(GroupServer group) {
		if (this.track.isEmpty()) {
			LoginServer.log.info("Playlist is empty, adding a random song");
			final Playlist p = Playlist.create(1, this.current.getTag_idstr());
			this.merge(p);
		}
		this.current = this.track.pop();
		this.current.setGroup(group);
		this.playTimer
				.schedule(this.current, this.current.getDuration() * 1000);
		LoginServer.log.info("Playing \"" + this.current.getName() + "\" for "
				+ this.current.getDuration() + " seconds");
	}

	@Override
	public String toString() {
		final TransformerFactory tf = TransformerFactory.newInstance();
		try {
			final Transformer serializer = tf.newTransformer();
			final DOMSource source = new DOMSource(this.marshal());
			final StreamResult result = new StreamResult(new StringWriter());
			serializer.transform(source, result);
			return result.getWriter().toString();
		} catch (final TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (final TransformerException e) {
			e.printStackTrace();
		}
		return null;
	}
}
